<template>
<div>
  <a-spin :spinning="loading.header">
    <a-page-header
      style="border: 1px solid rgb(235, 237, 240)"
      :title="appInfo.appName"
      @back="() => router.back()"
    >
      <a-descriptions title="用应用信息">
        <a-descriptions-item label="应用标识">{{ appInfo.ssoAppId }}</a-descriptions-item>
        <a-descriptions-item label="应用名字">{{ appInfo.appName }}</a-descriptions-item>
        <a-descriptions-item label="应用类型">{{ appInfo.appType }}</a-descriptions-item>
        <a-descriptions-item label="应用状态">
          <a-tag color="green" v-if="appInfo.status==null||appInfo.status==0">启用</a-tag>
          <a-tag color="#f50" v-else>禁用</a-tag>
        </a-descriptions-item>
        <a-descriptions-item label="客户端 ID">  <a-tag color="#2db7f5" style="cursor: pointer"  @click="toClipboard(appInfo.clientId)">{{ appInfo.clientId }}
          <template #icon>
            <CopyOutlined />
          </template>
        </a-tag></a-descriptions-item>
        <a-descriptions-item label="客户端密钥"> <a-tag color="#87d068"  style="cursor: pointer"  @click="toClipboard(appInfo.clientSecret)">{{appInfo.clientSecret }}
          <template #icon>
            <CopyOutlined />
          </template>
        </a-tag></a-descriptions-item>
        <a-descriptions-item label="应用备注">{{ appInfo.remark }}</a-descriptions-item>
        <a-descriptions-item label="创建时间">{{ new Date(appInfo.createTime).toLocaleString() }}</a-descriptions-item>
        <a-descriptions-item label="更新时间">{{ new Date(appInfo.updateTime).toLocaleString() }}</a-descriptions-item>
      </a-descriptions>
    </a-page-header>
  </a-spin>


  <a-tabs v-model:activeKey="activeName">
    <a-tab-pane key="1">
      <template #tab>
        <span>
        <AppstoreOutlined />
          应用配置
        </span>
      </template>

      <div>
        <a-form
          ref="formRef"
          :model="appInfo"
          :label-col="{ span: 8 }"
          :wrapper-col="{ span: 8 }"
          style="padding-top: 20px"
        >
          <a-form-item label="应用名称" name="name">
            <a-input v-model:value="appInfo.appName" allow-clear placeholder="请输入分组名字" />
          </a-form-item>
          <a-form-item label="应用地址" name="appUrl">
            <a-input v-model:value="appInfo.appUrl" allow-clear placeholder="请输入app登录地址" />
          </a-form-item>
          <a-form-item label="应用图标" name="appIcon" >
            <a-upload
              name="avatar"
              list-type="picture-card"
              class="avatar-uploader"
              :show-upload-list="false"
              :action="beforeUpload"
            >
              <img v-if="appInfo.appIcon" style="height: 80px;width: 80px;" :src="appInfo.appIcon" alt="appIcon" />
              <div v-else>
                <loading-outlined v-if="loadingPhoto"></loading-outlined>
                <plus-outlined v-else></plus-outlined>
                <div class="ant-upload-text">上传</div>
              </div>
            </a-upload>
          </a-form-item>

          <a-form-item label="应用分组" name="ssoAppGroupId">
            <a-select
              v-model:value="appInfo.ssoAppGroupId"
              show-search
              placeholder="请选择分组名字"
              option-filter-prop="label"
              :options="appGroup"
            />
          </a-form-item>
          <a-form-item label="应用状态" name="ssoAppGroupId">
            <a-select
              v-model:value="appInfo.status"
              show-search
              placeholder="请选择应用状态"
              option-filter-prop="label"
              :options="statusOption"
            />
          </a-form-item>
          <a-form-item label="备注" name="remark" >
            <a-textarea  :auto-size="{ minRows: 2, maxRows: 5 }" v-model:value="appInfo.remark" allow-clear placeholder="请输入备注" />
          </a-form-item>

            <div style="justify-content: center;display: flex">
              <a-button type="primary" html-type="submit" @click="updateBaseInfo" >保存</a-button>
            </div>

        </a-form>
      </div>

    </a-tab-pane>
    <a-tab-pane key="2">
      <template #tab>
        <span>
         <LinkOutlined />
          协议配置
        </span>
      </template>
      <div>
        <a-collapse
          :bordered="true"
          default-active-key="1"
          style="margin-bottom: 20px"
        >
          <template #expandIcon="{ isActive }">
            <caret-right-outlined :rotate="isActive ? 90 : 0" />
          </template>
          <a-collapse-panel key="1" header="信息配置" >
            <a-form
              ref="formRef"
              :model="jwtConfig"
              :label-col="{ span: 8 }"
              :wrapper-col="{ span: 8 }"
              style="padding-top: 20px"
            >

              <a-form-item label="SSO地址"   help="业务系统中的JWT SSO地址，在单点登录时本系统将向该地址发送id_token信息，参数名为id_token，业务系统通过id_token与Public Key可获取业务系统中的用户信息，如果在业务系统（SP）发起登录，请求SP登录地址时如果携带redirect_uri参数，系统会检验合法性，成功后会将浏览器重定向到该地址，并携带id_token身份令牌。" name="remark" >
                <a-input v-model:value="jwtConfig.redirectUris" allow-clear placeholder="请输入" />
              </a-form-item>

              <a-form-item label="请求Key名"   help="GET/POST中的key名，默认id_token,为空直接拼接URL强制GET请求" name="remark" >
                <a-input v-model:value="jwtConfig.reqKey" allow-clear placeholder="请输入" />
              </a-form-item>


              <a-form-item label="请求方式"   help="指定向JWT应用发送 id_token 的请求方式。" >
                <a-select
                  show-search
                  v-model:value="jwtConfig.bindingType"
                  placeholder="请选择请求方式"
                  option-filter-prop="label"
                  :options="reqType"
                />
              </a-form-item>

              <a-form-item label="id_token  有效期" help="默认30分钟 id_token 用于鉴别用户身份，JWT格式，允许应用使用公钥自行验证用户身份。最小5分钟，最大24小时，过期后需要使用refresh_token 刷新，或重新登录。" name="remark" >
                <a-input v-model:value="jwtConfig.idTokenTimeToLive" allow-clear placeholder="请输入" addon-after="分钟"/>
              </a-form-item>

              <a-form-item label="id_token 签名算法" help="id_token 签名使用的非对称算法" name="remark" >
                <a-select
                  show-search
                  v-model:value="jwtConfig.idTokenSignatureAlgorithm"
                  placeholder="请选择算法名字"
                  option-filter-prop="label"
                  :options="algorithmOption"
                />
              </a-form-item>

              <a-space
                v-for="(item, index) in jwtConfig.jwtPayload"
                :key="item.key"
                align="center"
                style="justify-content: center;display: flex;align-items: center"
              >
                <a-form-item
                  label="名字"
                >
                  <a-input v-model:value="item.key" placeholder="请输入" />
                </a-form-item>
                <a-form-item
                  label="内容"
                >
                  <a-input v-model:value="item.value" placeholder="请输入" />
                </a-form-item>
                <a-form-item>

                <MinusCircleOutlined @click="removePayload(item)" />
                </a-form-item>

              </a-space>

              <a-form-item style="justify-content: center;display: flex">
                <a-button type="dashed" block @click="addPayload">
                  <PlusOutlined />
                  添加JwtPayload
                </a-button>
              </a-form-item>


              <a-space
                v-for="(item, index) in jwtConfig.jwtHeader"
                :key="item.key"
                align="center"
                style="justify-content: center;display: flex;align-items: center"
              >
                <a-form-item
                  label="名字"
                >
                  <a-input v-model:value="item.key" placeholder="请输入" />
                </a-form-item>
                <a-form-item
                  label="内容"
                >
                  <a-input   v-model:value="item.value" placeholder="请输入" />
                </a-form-item>
                <a-form-item>
                  <MinusCircleOutlined @click="removeHeader(item)" />
                </a-form-item>

              </a-space>

              <a-form-item style="justify-content: center;display: flex">
                <a-button type="dashed" block @click="addHeader">
                  <PlusOutlined />
                  添加JwtHeader
                </a-button>
              </a-form-item>

              <div style="justify-content: center;display: flex">
                <a-button type="primary" html-type="submit" @click="saveOidcConfig" >保存</a-button>
              </div>
            </a-form>
          </a-collapse-panel>
        </a-collapse>
        <a-collapse
          default-active-key="3"
          :bordered="true"
          style="margin-bottom: 20px"
        >
          <template #expandIcon="{ isActive }">
            <caret-right-outlined :rotate="isActive ? 90 : 0" />
          </template>
          <a-collapse-panel key="3" header="配置信息">
            <a-form
              ref="formRef"
              :model="appInfo"
              :label-col="{ span: 8 }"
              :wrapper-col="{ span: 8 }"
              style="padding-top: 20px"
            >
              <a-form-item label="Issuer" help="用于标识 token 发放来源的字段。同时是下述接口的 baseUrl。" name="remark" >
                <a-input-group compact>
                  <a-input :value="getIss(jwtConfig)" style="width: calc(100% - 50px)" disabled />
                  <a-tooltip title="复制">
                    <a-button  @click="toClipboard(getIss(jwtConfig))" >
                      <template #icon><CopyOutlined/></template>
                    </a-button>
                  </a-tooltip>
                </a-input-group>
              </a-form-item>
              <a-form-item label="公钥" help="用于对JWT信息验签" name="remark" >
                <a-input-group compact>
                  <a-textarea :value="formatPublicKey(jwtConfig.publicKey)" :rows="10" style="width: calc(100% - 50px)" disabled />
                  <a-tooltip title="复制">
                    <a-button  @click="toClipboard(formatPublicKey(jwtConfig.publicKey))">
                      <template #icon><CopyOutlined /></template>
                    </a-button>
                  </a-tooltip>
                </a-input-group>
              </a-form-item>
            </a-form>
          </a-collapse-panel>
        </a-collapse>
      </div>
    </a-tab-pane>
    <a-tab-pane key="3">
      <template #tab>
        <span>
          <SafetyCertificateOutlined />
        访问授权
        </span>
      </template>
    <div>
      <div>
        <a-card class="mt-2">
          <a-button type="primary" class="mb-4" :icon="h(PlusOutlined)" @click="handleAdd">
            添加
          </a-button>
          <a-table
            bordered
            :columns="columns"
            :data-source="data"
            :pagination="pagination"
            :loading="tableLoading"
          >
            <template #bodyCell="{ column, record }">

              <template v-if="column.dataIndex === 'enabled'">
                <a-checkbox @change="changeEnabled(record)" v-model:checked="record.enabled">启用</a-checkbox>
              </template>

              <template v-if="column.dataIndex === 'subjectId'&&record.subObj">
                <div v-if="record.subjectType=='USER'">{{ record.subObj.name }}</div>
                <div v-if="record.subjectType=='USER_GROUP'">{{ record.subObj.name }}</div>
                <div v-if="record.subjectType=='ORGANIZATION'">{{ record.subObj.name }}</div>
              </template>

              <template v-if="column.dataIndex === 'subjectType'">
                <a-tag color="#2db7f5" v-if="record.subjectType=='USER'">用户</a-tag>
                <a-tag color="#87d068" v-if="record.subjectType=='USER_GROUP'">用户组</a-tag>
                <a-tag color="#108ee9" v-if="record.subjectType=='ORGANIZATION'">组织</a-tag>
              </template>
              <template v-if="column.dataIndex === 'actions'">
                <a-button size="small" type="link" danger @click="handleDelete(record)">移除授权</a-button>
              </template>
            </template>
          </a-table>
        </a-card>
        <a-modal v-model:open="showModal" :mask-closable="false" :title="title">
          <a-form
            ref="formRef"
            :model="form"
            :rules="rules"
            :label-col="{ span: 5 }"
            style="padding-top: 20px"
          >
            <a-form-item label="授权类型" name="subjectType">
              <a-radio-group v-model:value="form.subjectType" :options="subjectTypeOption" />
            </a-form-item>
            <a-form-item v-if="form.subjectType=='USER'" label="授权用户" name="subjectType">
              <a-select
                v-model:value="form.subjectId"
                show-search
                placeholder="输入关键字搜索"
                style="width: 60%"
                :default-active-first-option="false"
                :show-arrow="false"
                :filter-option="false"
                :not-found-content="null"
                :options="dataUser"
                @search="handleSearch"
                @change="handleChange"
              ></a-select>
            </a-form-item>
            <a-form-item v-if="form.subjectType=='USER_GROUP'" label="授权用户组" name="subjectType">
              <a-select
                v-model:value="form.subjectId"
                show-search
                style="width: 60%"
                :dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
                placeholder="请选择"
                allow-clear
                tree-default-expand-all
                :options="tree1"
                :fieldNames="treeFieldNames1"
              >
              </a-select>
            </a-form-item>
            <a-form-item v-if="form.subjectType=='ORGANIZATION'" label="授权组织" name="subjectType">
              <a-tree-select
                v-model:value="form.subjectId"
                show-search
                :dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
                placeholder="请选择"
                allow-clear
                style="width: 60%"
                tree-default-expand-all
                :tree-data="tree2"
                :fieldNames="treeFieldNames2"
              >
              </a-tree-select>
              <template #title="{ name }">
                <span>{{ name }}</span>
              </template>
            </a-form-item>
          </a-form>
          <template #footer>
            <a-button type="primary" :loading="formBtnLoading" @click="handleConfirm">提交</a-button>
          </template>
        </a-modal>
      </div>


    </div>
    </a-tab-pane>
  </a-tabs>
</div>
</template>
<script setup lang="ts">
import {useRoute} from "vue-router";
import SsoAppApi from "@/api/SsoAppApi";
import {h, onMounted, reactive, ref, watch} from "vue";
import {SsoApp, SsoAppGroup, SsoAppJwtConfig} from "@/types/entity";
import router from "@/router";
import {
  AppstoreOutlined,
  LinkOutlined,
  SafetyCertificateOutlined,
  CaretRightOutlined,
  CopyOutlined,
  MinusCircleOutlined,
  PlusOutlined,
  LoadingOutlined
} from "@ant-design/icons-vue";
import SsoAppGroupApi from "@/api/SsoAppGroupApi";
import useClipboard from 'vue-clipboard3'
import SsoAppOidcConfigApi from "@/api/SsoAppOidcConfigApi";
import {message, Modal} from "ant-design-vue";
import SsoAppAccessPolicyApi from "@/api/SsoAppAccessPolicyApi";
import {Rule} from "ant-design-vue/es/form";
import {resetReactive} from "@/utils";
import SsoOrganizationApi from "@/api/SsoOrganizationApi";
import SsoUserGroupApi from "@/api/SsoUserGroupApi";
import SsoUserApi from "@/api/SsoUserApi";
import SsoAppJwtConfigApi from "@/api/SsoAppJwtConfigApi";
import ResApi from "@/api/Res";

const jwtConfig=ref<SsoAppJwtConfig>({});

const { toClipboard } = useClipboard()
const appInfo=ref<SsoApp>({})
const loading=reactive({
  header:false
})
const appId=ref(useRoute().query.ssoAppId as string)
const appGroup=ref([])
const activeName = ref("1")
const getAppInfo=async ()=>{
  loading.header=true
  const  res = await SsoAppApi.getById(appId.value)
  appInfo.value=res.data[0]
  loading.header=false
}

const statusOption=[
  {
    value: 0,
    label: '启用',
  },
  {
    value: 1,
    label: '禁用',
  },
]

const reqType=[
  {
    value: 'GET',
    label: 'GET',
  },
  {
    value: 'POST',
    label: 'POST',
  },
]

const algorithmOption=[
  {
    value: 'RS256',
    label: 'RS256',
  },
  {
    value: 'ES256',
    label: 'ES256',
  },
  {
    value: 'HS256',
    label: 'HS256',
  },
]

const subjectTypeOption=[
  {
    value: 'USER',
    label: '用户',
  },
  {
    value: 'USER_GROUP',
    label: '用户组',
  },
  {
    value: 'ORGANIZATION',
    label: '组织',
  },
]

const loadOption=()=>{
  SsoAppGroupApi.list(null).then(res=>{
    appGroup.value=res.data
      .map((i: SsoAppGroup) => {
        return {
          label: i.name,
          value: i.ssoAppGroupId,
        };
      });
  })
}

const updateBaseInfo=async ()=>{
  await SsoAppApi.update(appInfo.value)
  await getAppInfo()
  message.success('操作成功');
}

const loadJwtInfo=()=>{
  SsoAppJwtConfigApi.getById(appId.value).then(res=>{
      if (res.data.length>0){
        jwtConfig.value=res.data[0]
        if (jwtConfig.value==null){
          jwtConfig.value={ }
        }
      }
  })
}

const saveOidcConfig=()=>{
  jwtConfig.value.ssoAppId=appId.value
  SsoAppJwtConfigApi.save(jwtConfig.value).then(res=>{
    message.success('操作成功');
    loadJwtInfo()
  })
}

const getIss=(item:any)=>{
  return item.iss+"/jwt/"+item.ssoAppId+"/sso"
}

const formatPublicKey=(pubKey:string)=> {
  // 去掉空格和换行符
  let formattedKey = pubKey.replace(/\s+/g, '');

  // 添加换行，使其符合标准的PEM格式，每64个字符一行
  let formattedKeyWithNewLines = '';
  for (let i = 0; i < formattedKey.length; i += 64) {
    formattedKeyWithNewLines += formattedKey.substring(i, i + 64) + '\n';
  }

  // 如果公钥包含 "BEGIN PUBLIC KEY" 和 "END PUBLIC KEY" 标签，添加上去
  formattedKeyWithNewLines = '-----BEGIN PUBLIC KEY-----\n' + formattedKeyWithNewLines + '-----END PUBLIC KEY-----';

  return formattedKeyWithNewLines;
}


onMounted(async ()=>{
  await getAppInfo()
  loadOption()
  loadJwtInfo()
  getList();
  getOrganization()
  getUserGroup()
})


const treeFieldNames2 = ref(	{children:'children', label:'name', value:'ssoOrganizationId' })
const treeFieldNames1 = ref(	{ label: 'name', value: 'ssoUserGroupId',})


let columns = [
  { title: '授权主体', dataIndex: 'subjectId' },
  { title: '主体类型', dataIndex: 'subjectType' },
  { title: '启用', dataIndex: 'enabled' },
  { title: '创建时间', dataIndex: 'createTime' },
  { title: '更新时间', dataIndex: 'updateTime' },
  {
    title: '操作',
    dataIndex: 'actions',
    align: 'center',
  },
];
let tree1 = ref([]);
let tree2 = ref([]);
let data = ref([]);
const pagination = reactive({
  current: 1,
  pageSize: 10,
  total: 0,
  onChange: (current, pageSize) => {
    pagination.current = current;
    pagination.pageSize = pageSize;
    getList();
  },
});

const listQuery = reactive<any>({});

const tableLoading = ref(false);


const getOrganization = ()=>{
  SsoOrganizationApi.getListTree().then(res=>{
    tree2.value = res.data[0].tree;
  })
}

const getUserGroup = ()=>{
  SsoUserGroupApi.list(null).then(res=>{
    tree1.value = res.data
  })
}


function getList() {
  tableLoading.value = true;
  listQuery.ssoAppId=appId.value
  SsoAppAccessPolicyApi.page({ index: pagination.current, size: pagination.pageSize, ...listQuery })
    .then((res) => {
      data.value = res.data;
      pagination.total = res.total;
    })
    .finally(() => (tableLoading.value = false));
}

const loadingPhoto=ref(false)
const showModal = ref(false);
const title = ref('');
const formRef = ref();
let form = reactive<any>({});
const rules: Record<string, Rule[]> = {
  name: [{ required: true, trigger: 'blur', message: '请输入' }],
};

function handleAdd() {
  showModal.value = true;
  title.value = '添加';
  resetReactive(form);
  formRef.value?.clearValidate();
}

const formBtnLoading = ref(false);

function handleConfirm() {
  formRef.value
    .validate()
    .then(() => {
      formBtnLoading.value = true;
      form.ssoAppId=appId.value as string
      (form.ssoAccessPolicyId ? SsoAppAccessPolicyApi.update(form) : SsoAppAccessPolicyApi.save(form))
        .then(() => {
          message.success('提交成功');
          showModal.value = false;
          getList();
        })
        .finally(() => {
          formBtnLoading.value = false;
        });
    })
    .catch(() => {});
}

function handleDelete(row) {
  Modal.confirm({
    title: '提示',
    content: '确定删除这条记录吗',
    onOk() {
      SsoAppAccessPolicyApi.delete([row.ssoAccessPolicyId]).then(() => {
        message.success('操作成功');
        getList();
      });
    },
    onCancel() {},
  });
}



let timeout: any;
let currentValue = '';

function fetch(value: string, callback: any) {
  if (timeout) {
    clearTimeout(timeout);
    timeout = null;
  }
  currentValue = value;

  function fake() {
    SsoUserApi.listKeyword(value).then(res=>{
          if (currentValue === value) {
            const result = res.data;
            const data: any[] = [];
            result.forEach((r: any) => {
              data.push({
                value: r.ssoUserId,
                label: r.username+"=>"+r.name,
              });
            });
            callback(data);
          }
    })

  }

  timeout = setTimeout(fake, 300);
}
const dataUser = ref<any[]>([]);
const valueUser = ref();

const handleSearch = (val: string) => {
  fetch(val, (d: any[]) => (dataUser.value = d));
};

const handleChange = (val: string) => {
  valueUser.value = val;
  fetch(val, (d: any[]) => (dataUser.value = d));
};

const changeEnabled = (val)=>{
  SsoAppAccessPolicyApi.updateEnabled(val).then(res=>{
    message.success("更改成功")
    getList()
  })

}

watch( () =>form.subjectType,()=>{
  console.log(form.subjectType)
  form.subjectId='';
})


const removePayload = (item)=>{
  const index = jwtConfig.value.jwtPayload.indexOf(item);
  if (index !== -1) {
    jwtConfig.value.jwtPayload.splice(index, 1);
  }
}
const addPayload = () => {
  if (!jwtConfig.value.jwtPayload){
    jwtConfig.value.jwtPayload=[]
  }
  jwtConfig.value.jwtPayload.push({
    key: '',
    value: '',
  });
};


const removeHeader = (item)=>{
  const index = jwtConfig.value.jwtHeader.indexOf(item);
  if (index !== -1) {
    jwtConfig.value.jwtHeader.splice(index, 1);
  }
}

const addHeader = () => {
  if (!jwtConfig.value.jwtHeader){
    jwtConfig.value.jwtHeader=[]
  }
  jwtConfig.value.jwtHeader.push({
    key: '',
    value: '',
  });
};


function beforeUpload(file){
  loadingPhoto.value=true
  ResApi.upLoad(file).then(res=>{
    console.log(res)
    if (res.code==="SUCCESS"){
      appInfo.value.appIcon=res.data[0]
    }

  }).finally(()=>{
    loadingPhoto.value=false
  })
}


</script>

<style scoped>




</style>
